/* 
 * Copyright 2010 Ziya Guven Koylu, Ozge Koylu
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at 
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package com.koylu.caffein.javassist;

import java.util.Locale;

import javassist.ClassClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtNewMethod;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

public class BeanGenerator {
	
	private static Log log = LogFactory.getLog(BeanGenerator.class);
	private CtClass ctClass = null;
	private Class<?> generatedClass = null;
	
	public BeanGenerator(String superClass, String className) throws Exception{
		if(isClassExist(superClass)){
			if(!isClassExistInClassPool(superClass)){
				addToClassPoolPath(superClass);
			}	
		}else{
			throw new ClassNotFoundException(superClass);
		}
		ClassPool classPool = ClassPool.getDefault();
		ctClass = classPool.makeClass(makeClassName(className));
		ctClass.setSuperclass(classPool.get(superClass));
	}

	public BeanGenerator(String className) throws Exception{
		ctClass = ClassPool.getDefault().makeClass(makeClassName(className));
	}
	
	public void addProperty(String fieldName, Class<?> fieldType) throws Exception{
		log.info("adding property to class:"+ctClass.getName()+" fieldName:"+fieldName+"-->"+fieldType.getName());
		ctClass.addField(new CtField(ClassPool.getDefault().get(fieldType.getCanonicalName()), fieldName, ctClass));
		String methodName = formatField(fieldName);
		ctClass.addMethod(CtNewMethod.make(new StringBuffer("public void set").append(methodName).append("(").append(fieldType.getCanonicalName()).append(" ").append(fieldName).append("){this.").append(fieldName).append("=").append(fieldName).append(";}").toString(), ctClass));
		ctClass.addMethod(CtNewMethod.make(new StringBuffer("public ").append(fieldType.getCanonicalName()).append(" get").append(methodName).append("(").append("){return this.").append(fieldName).append(";}").toString(), ctClass));
	}
	
	public Class<?> createClass() throws Exception{
		if(generatedClass == null){
			generatedClass = ctClass.toClass();
			ctClass.detach();
		}
		return generatedClass;
	}
	
	public static boolean isClassExist(String className){
		try {
			Class.forName(className);
			return true;
		} catch (ClassNotFoundException e) {
			return false;
		}
	}
	
	private static boolean isClassExistInClassPool(String className){
		return ClassPool.getDefault().find(className) != null;
	}
	
	private static void addToClassPoolPath(String className) throws Exception{
		ClassPool.getDefault().insertClassPath(new ClassClassPath(Class.forName(className)));
	}
	
	private static int counter = 0;
	private static synchronized String makeClassName(String className) {
		return className + "_$$_caffein_" + counter++ +"_"+ System.currentTimeMillis();
	}
	
	public static String formatField(String fieldName){
		return fieldName.substring(0, 1).toUpperCase(Locale.ENGLISH)+fieldName.substring(1, fieldName.length());
	}
}